package com.ly.service;

import com.ly.component.threadpool.util.SpringTaskManager;
import com.ly.model.entity.CourseDetailInfo;
import com.ly.model.entity.CourseInfo;
import com.ly.model.enums.WaitType;
import com.ly.utils.MyObjectUtil;
import lombok.Getter;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.firefox.FirefoxOptions;
import org.openqa.selenium.firefox.FirefoxProfile;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;

@SuppressWarnings("all")
public class WebDriverService {
    public CourseService courseService;
    private WebDriver driver;
    private WebDriverWait wait;
    @Getter
    private String name;
    private CaptchaService captchaService;
    private CourseQueueService courseQueueService;
    private ConfigService configService;

    public WebDriverService(
            String name,
            ConfigService configService,
            CourseQueueService courseQueueService,
            CaptchaService captchaService
                           ) {
        this.name = name;
        this.configService = configService;
        this.courseQueueService = courseQueueService;
        this.captchaService = captchaService;
        initWebDirver();
    }

    public void initWebDirver() {
        int browser = 0;
        if ("firefox".equalsIgnoreCase(configService.configEntity.getBrowser())) {
            browser = 1;
        } else if ("chrome".equalsIgnoreCase(configService.configEntity.getBrowser())) {
            browser = 2;
        }
        switch (browser) {
            case 1:
                if (configService.configEntity != null) {
                    if (configService.configEntity.getDriver() != null) {
                        System.setProperty("webdriver.gecko.driver", configService.configEntity.getDriver());
                    }
                    if (configService.configEntity.getBin() != null) {
                        System.setProperty("webdriver.firefox.bin", configService.configEntity.getBin());
                    }
                }
                System.setProperty(FirefoxDriver.SystemProperty.BROWSER_LOGFILE, "/dev/null");
                FirefoxOptions firefoxOptions = new FirefoxOptions();
                firefoxOptions.setHeadless(true);
                FirefoxProfile firefoxProfile = new FirefoxProfile();
                firefoxProfile.setPreference("media.volume_scale", "0.0");
                firefoxOptions.setProfile(firefoxProfile);
                driver = new FirefoxDriver(firefoxOptions);
                break;
            case 2:
                if (configService.configEntity != null) {
                    if (configService.configEntity.getDriver() != null) {
                        System.setProperty("webdriver.chrome.driver", configService.configEntity.getDriver());
                    }
                    if (configService.configEntity.getBin() != null) {
                        System.setProperty("webdriver.chrome.bin", configService.configEntity.getBin());
                    }
                }
                ChromeOptions chromeOptions = new ChromeOptions();
                chromeOptions.setHeadless(true);
                chromeOptions.addArguments("--mute-audio");
                driver = new ChromeDriver(chromeOptions);
                break;
            default:
                throw new RuntimeException("请配置浏览器");
        }
        wait = new WebDriverWait(driver, 120);
    }

    public void startLogin() {
        WebDriverWait checkLogin = new WebDriverWait(driver, 3);
        WebElement success = null;
        do {
            login();
            try {
                success = checkLogin.until(ExpectedConditions.elementToBeClickable(By.linkText("主题专栏")));
            } catch (Exception e) {
                success = null;
                System.out.println(String.format("%s:验证码输入错误", name));
            }
        } while (success == null);
        initCourseService();
    }

    public void start() throws Exception {
        while (courseQueueService.courseInfos == null || !courseQueueService.courseInfos.isEmpty()) {
            viewCourse();
        }
        System.out.println(String.format("%s:处理结束", name));
        System.exit(0);
    }

    public void stop() {
        System.out.println(String.format("%s:执行停止操作", name));
        try {
            driver.quit();
        } catch (Exception e) {
        }
        try {
            courseService.stop();
        } catch (Exception e) {

        }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
        }
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        stop();
    }

    public void login() {
        driver.get("http://wsxy.chinaunicom.cn/learner/login");
        if (configService.configEntity != null) {
            if (configService.configEntity.getUsername() != null) {
                getElement(By.cssSelector(".ant-tabs-tabpane:nth-child(1) .ant-form-item:nth-child(1) .ant-input"), WaitType.VISIBLE)
                        .sendKeys(configService.configEntity.getUsername());
            }
            if (configService.configEntity.getPassword() != null) {
                getElement(By.cssSelector(".ant-tabs-tabpane:nth-child(1) .ant-form-item:nth-child(2) .ant-input"), WaitType.VISIBLE)
                        .sendKeys(configService.configEntity.getPassword());
            }

            WebElement code = getElement(By.cssSelector("nz-tab-body.ant-tabs-tabpane:nth-child(1) > form:nth-child(1) > div:nth-child(3) > div:nth-child(2) > img:nth-child(2)"), WaitType.VISIBLE);
            BufferedImage captchaImg = catchEleImg(code);
            String captchaCode = captchaService.getCaptchaByServer(captchaImg, configService.configEntity.getCaptchaServer());
            if (captchaCode == null) {
                System.out.println("请输入验证码:");
                captchaCode = captchaService.getCaptchaByHuman(captchaImg);
            }
            if (captchaCode != null) {
                getElement(By.cssSelector(".ng-dirty .input-box"), WaitType.VISIBLE).sendKeys(captchaCode);
            } else {
                throw new RuntimeException("验证码异常");
            }
            getElement(By.cssSelector(".ng-dirty .login-form-button"), WaitType.CLICK).click();
        }
    }

    private void initCourseService() {
        courseService = new CourseService(driver.manage().getCookies(), configService.configEntity.getCourseInfoPath());
    }

    private void viewCourse() {
        CourseInfo courseInfo = popMaxPeriod();
        if (courseInfo != null) {
            System.out.println(String.format("%s:处理课程: %s", name, courseInfo.getName()));
            playCourse(courseInfo);
            CourseDetailInfo courseDetail = courseService.getCourseDetail(courseInfo.getOfferingCourseId());
            if (courseDetail != null) {
                SpringTaskManager.getInstance().submit(courseDetail.getCourseId() + "", null, params -> {
                    CourseDetailInfo cd = courseService.getCourseDetail(courseInfo.getOfferingCourseId());
                    int current = 0;
                    double progress[] = {-1.0, -1.0};
                    progress[current] = MyObjectUtil.getOrDefault(cd.getProgress(), 0.0);
                    int count = 0;
                    int retry = 0;
                    while (true) {
                        try {
                            if (progress[current] == progress[1 - current]) {
                                count++;
                            } else {
                                count = 0;
                            }
                            if (count > 3) {
                                count = 0;
                                System.out.println(String.format("%s:播放失败,重新加载视频: %s", name, courseInfo.getName()));
                                playCourse(courseInfo);
                                retry++;
                            } else {
                                retry = 0;
                            }
                            if (retry > 10) {
                                System.out.println(String.format("%s:播放失败, 重复次数超上限, 停止播放: %s", name, courseInfo.getName()));
                                break;
                            }
                            getElement(By.cssSelector(".save-logout-box"), WaitType.CLICK).click();
                            cd = courseService.getCourseDetail(courseInfo.getOfferingCourseId());
                            progress[1 - current] = cd.getProgress();
                            current = 1 - current;
                            System.out.println(String.format(
                                    "%s:课程: %s, 保存学习记录, 当前学习进度: %s", name, courseInfo.getName(),
                                    progress[current] + "%"
                                                            ));
                            Thread.sleep(60000);
                        } catch (Exception e) {
                        }
                    }
                    return true;
                }, params -> true);
                try {
                    int first = 0;
                    while (first == 0 || (
                            courseDetail.getProgress() == null ||
                            courseDetail.getProgress() != 100 && SpringTaskManager.getInstance().isAlive(
                                    courseDetail.getCourseId() + "")
                    )) {
                        first = 1;
                        long sleepTime = (long) (
                                courseDetail.getDuration() * 600.0 * (100.0 - courseDetail.getProgress())
                        );
                        if (sleepTime < 1000) {
                            sleepTime = 1000;
                        }
                        if (sleepTime > 120000) {
                            sleepTime = 120000;
                        }
                        Thread.sleep(sleepTime);
                        CourseDetailInfo cd1 = courseService.getCourseDetail(courseInfo.getOfferingCourseId());
                        if (cd1 != null) {
                            courseDetail = cd1;
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                SpringTaskManager.getInstance().cancel(courseDetail.getCourseId() + "");
                //保存学习记录
                System.out.println(String.format("%s:保存学习记录: %s", name, courseInfo.getName()));
                getElement(By.cssSelector(".save-logout-box"), WaitType.CLICK).click();
                //退出
                System.out.println(String.format("%s:退出: %s", name, courseInfo.getName()));
                getElement(By.cssSelector(".anticon-arrow-left"), WaitType.CLICK).click();
            }
        }
    }

    private void playCourse(CourseInfo courseInfo) {
        String path = String.format("http://wsxy.chinaunicom.cn/learner/course/detail/%d", courseInfo.getOfferingCourseId());
        driver.get(path);
        try {
            getElement(By.xpath("/html/body/spk-root/spk-home/div/nz-spin/div[2]/spk-course/spk-detail/div/div/nz-spin/div[2]/div/div[3]/div[3]/button"), WaitType.CLICK)
                    .click();
        } catch (Exception e) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException interruptedException) {
                interruptedException.printStackTrace();
            }
        }
    }

    private WebElement getElement(By by, WaitType waitType) {
        try {
            switch (waitType) {
                case CLICK:
                    return wait.until(ExpectedConditions.elementToBeClickable(by));
                case VISIBLE:
                    return wait.until(ExpectedConditions.visibilityOfElementLocated(by));
            }
        } catch (Exception e) {
            return null;
        }
        return driver.findElement(by);
    }


    private BufferedImage catchEleImg(WebElement element) {
        File file = element.getScreenshotAs(OutputType.FILE);
        try {
            BufferedImage img = ImageIO.read(file);
            file.delete();
            return img;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    private CourseInfo popMaxPeriod() {
        return courseQueueService.popCourseInfo();
    }

    public Deque<CourseInfo> getCourseInfo() {
        return new ConcurrentLinkedDeque<>(courseService.page(0, 1000));
    }
}
